/**
 * PHOSIDE: PHosphorylation Site IDentification Engine.
 * Copyright 2009 Digital Biology Lab, University of Missouri.
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version. <p/> This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more
 * details.
 */

package phoside;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import phoside.util.StaticFinalValues;

/**
 *
 * @author gjj
 */
public class PhosphoProteinsImpl extends ProteinsImpl
                             implements PhosphoProteins {
    public PhosphoProteinsImpl() {
        this(null);
    }

    public PhosphoProteinsImpl(Proteins proteins) {
        this(proteins, false, (Set)null);
    }

    public PhosphoProteinsImpl(Proteins proteins, boolean deepCopy, String[] fields) {
        this(proteins, deepCopy, fields==null?null:new HashSet(Arrays.asList(fields)));
    }

    public PhosphoProteinsImpl(Proteins proteins, boolean deepCopy, Set<String> fields) {
        super(proteins, deepCopy, fields);
    }

    /**
     * {@inheritDoc}
     */
    public Set<Integer> getSites(final String proteinAccession) {
        if (proteinAccession==null) {
            throw new NullPointerException("proteinAccession cannot be null");
        }

        Protein protein = getProtein(proteinAccession);
        if (protein==null)
            return null;

        Object obj = protein.getInfo(StaticFinalValues.SITES);
        if (obj==null)
            return new TreeSet();

        try {
            return (Set<Integer>)obj;
        } catch (java.lang.ClassCastException e) {
            return new TreeSet();
        }
    }

    /**
     * {@inheritDoc}
     */
    public List<String> getSiteNotes(String proteinAccession, int site) {
        if (proteinAccession==null) {
            throw new NullPointerException("proteinAccession cannot be null");
        }

        Protein protein = getProtein(proteinAccession);


        Object obj = protein.getInfo(StaticFinalValues.SITENOTES);
        if (obj==null) {
            return null;
        }

        Map<Integer,List<String>> mapSiteKinases = (Map<Integer,List<String>>)obj;

        return mapSiteKinases.get(site);
    }

    /**
     * {@inheritDoc}
     */
    public void addSite(final String proteinAccession, final int site) {
        addSite(proteinAccession, site, null);
    }

    /**
     * {@inheritDoc}
     */
    public void addSite(final String proteinAccession, final int site, List<String> notes) {
        if (proteinAccession==null) {
            throw new NullPointerException("proteinAccession cannot be null");
        }

        Protein protein = mapAccessionProtein.get(proteinAccession);
        if (protein==null) {
            throw new IllegalStateException("Add protein first: "+proteinAccession);
        }

        String proteinSeq = protein.getSequence();
        if (site<0 || site>=proteinSeq.length()) {
            throw new IndexOutOfBoundsException("Site out of protein boundary.\n" +
                    "Protein:"+proteinAccession+", site:"+site);
        }

        char c = proteinSeq.charAt(site);
        if (!PhosphoProteins.PhosphoType.phosphorylatableAminoAcids().contains(c)) {
            throw new IllegalStateException("This amino acid cannot be phosphorylated.\n" +
                    "Protein:"+proteinAccession+", site:"+site);
        }

        Set<Integer> sites = getSites(proteinAccession);
        if (sites==null || sites.isEmpty()) {
            sites = new TreeSet<Integer>();
            protein.putInfo(StaticFinalValues.SITES, sites);
        }

        sites.add(site);

        if (notes==null||notes.isEmpty())
            return;

        Map<Integer,List<String>> mapSiteNotes;
        Object obj = protein.getInfo(StaticFinalValues.SITENOTES);
        if (obj!=null) {
            mapSiteNotes = (Map<Integer,List<String>>)obj;
        } else {
            mapSiteNotes = new HashMap();
            protein.putInfo(StaticFinalValues.SITENOTES, mapSiteNotes);
        }

        List<String> currNotes = mapSiteNotes.get(site);
        if (currNotes==null) {
            currNotes = new ArrayList();
            mapSiteNotes.put(site, currNotes);
        }
        currNotes.addAll(notes);
    }

    /**
     * {@inheritDoc}
     */
    public void addSites(final String proteinAccession, final Set<Integer> sites) {
        if (proteinAccession==null) {
            throw new NullPointerException("proteinAccession cannot be null");
        }

        if (sites==null) {
            throw new NullPointerException("Null sites");
        }

        Protein proInfo = mapAccessionProtein.get(proteinAccession);
        if (proInfo==null) {
            throw new IllegalStateException("Add protein first.");
        }

        String proteinSeq = proInfo.getSequence();
        for (int site : sites) {
            if (site<0 || site>=proteinSeq.length()) {
                throw new IndexOutOfBoundsException("Site out of protein boundary.");
            }

            char c = proteinSeq.charAt(site);
            if (!PhosphoProteins.PhosphoType.phosphorylatableAminoAcids().contains(c)) {
                throw new IllegalStateException("the amino acid '"+c+"' cannot be phosphorylated.");
            }
        }

        Set<Integer> curr_sites = getSites(proteinAccession);
        if (curr_sites==null || curr_sites.isEmpty()) {
            curr_sites = new TreeSet<Integer>();
            proInfo.putInfo(StaticFinalValues.SITES, curr_sites);
        }

        curr_sites.addAll(sites);
    }

    /**
     * {@inheritDoc}
     */
    public void removeSites(String proteinAccession, Set<Integer> sites) {
        if (proteinAccession==null) {
            throw new NullPointerException("proteinAccession cannot be null");
        }

        if (sites==null) {
            throw new NullPointerException("Null sites");
        }

        Protein protein = mapAccessionProtein.get(proteinAccession);
        if (protein==null) {
            return;
        }

        Set<Integer> curr_sites = getSites(proteinAccession);
        if (curr_sites==null || curr_sites.isEmpty()) {
            return;
        }

        curr_sites.removeAll(sites);

        Object obj = protein.getInfo(StaticFinalValues.SITENOTES);
        if (obj!=null) {
            Map<Integer,List<String>> mapSiteNotes = (Map<Integer,List<String>>)obj;
            for (Integer site : sites)
                mapSiteNotes.remove(site);
        } 
    }

    /**
     * {@inheritDoc}
     */
    public void clearSites(String proteinAccession) {
        if (proteinAccession==null) {
            throw new NullPointerException("proteinAccession cannot be null");
        }

        Protein proInfo = mapAccessionProtein.get(proteinAccession);
        if (proInfo==null) {
            return;
        }

        proInfo.removeInfo(StaticFinalValues.SITES);
        proInfo.removeInfo(StaticFinalValues.SITENOTES);
    }

}
